/******************************************************************************* 
 * Copyright (c) 2005, 2015 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html  
 * 
 * Contributors: 
 *     Markus Schorn - initial API and implementation 
 *     IBM Corporation
 *     Sergey Prigogin (Google)
 ******************************************************************************/ 
package org.eclipse.cdt.internal.ui.refactoring.rename;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.services.IDisposable;

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElifStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IQualifierType;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.c.ICCompositeTypeScope;
import org.eclipse.cdt.core.dom.ast.c.ICFunctionPrototypeScope;
import org.eclipse.cdt.core.dom.ast.c.ICFunctionScope;
import org.eclipse.cdt.core.dom.ast.c.ICScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateTypeParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.core.dom.parser.ASTInternal;
import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.index.IIndexScope;
import org.eclipse.cdt.internal.corext.util.CModelUtil;

import org.eclipse.cdt.internal.ui.editor.ASTProvider;

/**
 * Used for refactoring to cache the IASTTranslationUnits.
 * Contains a collection of methods operating on ASTNodes.
 * The object has to be disposed of after use.
 */
public class ASTManager implements IDisposable {
	private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS
			| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT
			| ITranslationUnit.AST_SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS
			| ITranslationUnit.AST_PARSE_INACTIVE_CODE;

	public final static int TRUE= 1;
    public final static int FALSE= 0;
    public final static int UNKNOWN= -1;
    
    // TODO(sprigogin): Replace fSharedAST and fTranslationUnits with CRefactoringContext.
	private IASTTranslationUnit fSharedAST;
    private final Map<IFile, IASTTranslationUnit> fTranslationUnits= new HashMap<>();
    private final Set<String> fProblemUnits= new HashSet<>();
    private final CRefactoringArgument fArgument;
    private IBinding[] fValidBindings;
    private String fRenameTo;
    private HashMap<IBinding, Integer> fKnownBindings;
    private HashSet<IBinding> fConflictingBinding;
	private boolean fDisposed;

    public static String nth_of_m(int n, int m) {
        StringBuilder nofm= new StringBuilder();
        append_nth_of_m(n, m, nofm);
        return nofm.toString();
    }

    static void append_nth_of_m(int n, int m, StringBuilder buf) {
        buf.append(n);
        switch (n) {
            case 1:
                buf.append("st"); //$NON-NLS-1$
                break;
            case 2:
                buf.append("nd"); //$NON-NLS-1$
                break;
            case 3:
                buf.append("rd"); //$NON-NLS-1$
                break;
            default:
                buf.append("th"); //$NON-NLS-1$
                break;
        }
        buf.append(" of "); //$NON-NLS-1$
        buf.append(m);
    }
    
    public static IASTFileLocation getLocationInTranslationUnit(IASTNode node) {
        return node.getFileLocation();
    }    

    public static IASTName getSimpleName(IASTName name) {
        if (name instanceof ICPPASTQualifiedName) {
            if (name.getLastName() != null) {
                name= name.getLastName();
            }
        }
        return name;
    }
    
    /**
     * Returns TRUE, FALSE or UNKNOWN.
     * @throws DOMException 
     */
    public static int isSameBinding(IIndex index, IBinding b1, IBinding b2) throws DOMException {
        if (b1 == null || b2 == null) {
            return UNKNOWN;
        }
        if (b1.equals(b2)) {
            return TRUE;
        }
        if (b1 instanceof IIndexBinding || b2 instanceof IIndexBinding) {
        	if (index != null) {
        		IIndexBinding b11 = index.adaptBinding(b1);
        		if (b11 != null)
        			b1= b11;
        		IIndexBinding b21 = index.adaptBinding(b2);
        		if (b21 != null)
        			b2= b21;
                if (b1.equals(b2))
                	return TRUE;
        	}
        }
        
        String n1= b1.getName();
        String n2= b2.getName();
        if (n1 == null || n2 == null) {
            return UNKNOWN;
        }
        if (!n1.equals(n2)) {
            return FALSE;
        }
        if (b1 instanceof ICompositeType) {
            if (!(b2 instanceof ICompositeType)) {
                return FALSE;
            }
            ICompositeType c1= (ICompositeType) b1;
            ICompositeType c2= (ICompositeType) b2;
            if (c1.getKey() != c2.getKey()) {
                return FALSE;
            }
            IScope s1= c1.getCompositeScope();
            if (s1 != null) s1= s1.getParent();
            IScope s2= c2.getCompositeScope();
            if (s2 != null) s2= s2.getParent();
            return isSameScope(s1, s2, false);
        }

        if (b1 instanceof IFunction) {
            if (!(b2 instanceof IFunction)) {
                return FALSE;
            }
            boolean isStatic= false;
            boolean checkSig= true;
            IFunction c1= (IFunction) b1;
            IFunction c2= (IFunction) b2;
            if (b1 instanceof ICPPMethod) {
                if (!(b2 instanceof ICPPMethod)) {
                    return FALSE;
                }
            } else {
                if (b2 instanceof ICPPMethod) {
                    return FALSE;
                }
                isStatic= c1.isStatic() || c2.isStatic();
                if ((!(b1 instanceof ICPPFunction) || ((ICPPFunction) b1).isExternC()) &&
                		(!(b2 instanceof ICPPFunction) || ((ICPPFunction) b2).isExternC())) {
                    checkSig= false;
                }
            }
            
            int r1= isSameScope(b1.getScope(), b2.getScope(), isStatic);
            if (r1 == FALSE) {
                return FALSE;
            }
            
            int r2= checkSig ? hasSameSignature(c1, c2) : TRUE;
            if (r2 == FALSE) {
                return FALSE;
            }
            if (r1 != r2) {
                return UNKNOWN;
            }
            return r1;
        }
        
        if (b1 instanceof IVariable) {
            boolean fileStatic= false;
            if (!(b2 instanceof IVariable)) {
                return FALSE;
            }

            IVariable c1= (IVariable) b1;
            IVariable c2= (IVariable) b2;
            if (b1 instanceof IField) {
                if (!(b2 instanceof IField)) {
                    return FALSE;
                }
            } else if (b1 instanceof IParameter) {
                if (!(b2 instanceof IParameter)) {
                    return FALSE;
                }
            } else {
                if (b2 instanceof IField || b2 instanceof IParameter) {
                    return FALSE;
                }
                fileStatic= c1.isStatic() || c2.isStatic();
            }
            int result= isSameScope(c1.getScope(), c2.getScope(), fileStatic);
            return result == UNKNOWN ? TRUE : result;
        }

        if (b1 instanceof IEnumerator) {
            if (!(b2 instanceof IEnumerator)) {
                return FALSE;
            }
            return isSameScope(b1.getScope(), b2.getScope(), false);
        }
        
        if (b1 instanceof ITypedef) {
            if (!(b2 instanceof ITypedef)) {
                return FALSE;
            }
            return isSameScope(b1.getScope(), b2.getScope(), false);
        }
        
        if (b1 instanceof IMacroBinding) {
            if (!(b2 instanceof IMacroBinding)) {
                return FALSE;
            }
            return TRUE;
        }
        if (b1 instanceof IEnumeration) {
            if (!(b2 instanceof IEnumeration)) {
                return FALSE;
            }
            return isSameScope(b1.getScope(), b2.getScope(), false);
        }
        int scopeCmp= isSameScope(b1.getScope(), b2.getScope(), false);
        if (scopeCmp != TRUE) {
            return scopeCmp;
        }
        
        if (b1.getClass().equals(b2.getClass())) {
            return TRUE;
        }
        return UNKNOWN;
    }
    
    public static int isSameScope(IScope s1, IScope s2, boolean fileStatic) throws DOMException {
        if (s1 == s2) {
            return TRUE;
        }
        IASTNode node1= ASTInternal.getPhysicalNodeOfScope(s1);
        IASTNode node2= ASTInternal.getPhysicalNodeOfScope(s2);

        // Forward declarations do not have parent scopes.
        if (s1 == null) {
            if (!fileStatic && node2 instanceof IASTTranslationUnit) {
                return TRUE;
            }
            return UNKNOWN;
        }
        if (s2 == null) {
            if (!fileStatic && node1 instanceof IASTTranslationUnit) {
                return TRUE;
            }
            return UNKNOWN;
        }
        
        if (s1.equals(s2)) {
            return TRUE;
        }

        if (node1 instanceof IASTTranslationUnit && node2 instanceof IASTTranslationUnit) {
            return hasSameLocation(node1, node2, fileStatic);
        }

        if (s1.getKind() == EScopeKind.eGlobal && s2.getKind() == EScopeKind.eGlobal)
        	return TRUE;

        
        if (s1 instanceof ICPPBlockScope) {
            if (s2 instanceof ICPPBlockScope) {
                return hasSameLocation(node1, node2, fileStatic);
            }
            return FALSE;
        }

        String name1= getName(s1);
        String name2= getName(s2);
        if (s1 instanceof ICPPNamespaceScope) {
            if (s2 instanceof ICPPNamespaceScope) {
                ICPPNamespaceScope n1= (ICPPNamespaceScope) s1;
                ICPPNamespaceScope n2= (ICPPNamespaceScope) s2;
                int r1= hasSameLocation(node1, node2, fileStatic);
                if (r1 == TRUE) {
                    return r1;
                }
                if (name1 == null || name2 == null || !name1.equals(name2)) {
                    return FALSE;
                }
                return isSameScope(n1.getParent(), n2.getParent(), fileStatic);
            }
            return FALSE;
        }

        if (name1 != null && name2 != null && !name1.equals(name2)) {
            return FALSE;
        }

        // Classes.
        if (s1 instanceof ICPPClassScope || s1 instanceof ICCompositeTypeScope) {
            if (s2 instanceof ICPPClassScope || s2 instanceof ICCompositeTypeScope) {
                return isSameScope(s1.getParent(), s2.getParent(), fileStatic);
            }
            return FALSE;
        }
        // Functions.
        if (s1 instanceof ICPPFunctionScope) {
            if (s2 instanceof ICPPFunctionScope) {
                return hasSameLocation(node1, node2, true);
            }
            return FALSE;
        }
        if (s1 instanceof ICFunctionScope || s1 instanceof ICFunctionPrototypeScope
        		|| s1 instanceof ICScope) {
            if (s2 instanceof ICFunctionScope || s2 instanceof ICFunctionPrototypeScope
                    || s2 instanceof ICScope) {
                return hasSameLocation(node1, node2, true);
            }
            return FALSE;
        }
        
        return isSameScope(s1.getParent(), s2.getParent(), fileStatic);
    }

    public static String getName(IScope scope) {
        String name= null;
        if (scope instanceof IIndexScope) {
			IIndexScope indexScope= (IIndexScope) scope;
			final IIndexName scopeName = indexScope.getScopeName();
			if (scopeName != null) {
				name= scopeName.toString();
			}
		} else {
			name= getNameOrNull(ASTInternal.getPhysicalNodeOfScope(scope));
		}
        return name;
    }

    public static int hasSameSignature(IFunction f1, IFunction f2) throws DOMException {
    	if (f1.takesVarArgs() != f2.takesVarArgs())
    		return FALSE;

        if (f1 instanceof ICPPMethod != f2 instanceof ICPPMethod) 
        	return FALSE;

        return hasSameSignature(f1.getType(), f2.getType());
    }

    public static int hasSameSignature(IFunctionType t1, IFunctionType t2) throws DOMException {
    	if (t1 instanceof ICPPFunctionType && t2 instanceof ICPPFunctionType) {
    		ICPPFunctionType cppt1= (ICPPFunctionType) t1;
    		ICPPFunctionType cppt2= (ICPPFunctionType) t2;
    		if (cppt1.isConst() != cppt2.isConst())
    			return FALSE;
    		if (cppt1.isVolatile() != cppt2.isVolatile())
    			return FALSE;
    	}
        return isSameParameterList(t1.getParameterTypes(), t2.getParameterTypes());
    }        

    private static int isSameParameterList(IType[] p1, IType[] p2) throws DOMException {
        if (p1 == p2) {
            return TRUE;
        }
        if (p1 == null || p2 == null) {
            return UNKNOWN;
        }
        if (p1.length != p2.length) {
            return FALSE;
        }
        int retval= TRUE;
        for (int i = 0; i < p2.length; i++) {
            switch (isSameType(p1[i], p2[i])) {
            case FALSE:
                return FALSE;
            case UNKNOWN:
                retval= UNKNOWN;
                break;
            }
        }
        
        return retval;
    }

    private static int isSameType(IType t1, IType t2) throws DOMException {
        if (t1 != null && t2 != null && t1.isSameType(t2)) {
            return TRUE;
        }
        t1= getRealType(t1);
        t2= getRealType(t2);
        if (t1 == t2) {
            return TRUE;
        }
        if (t1 == null || t2 == null || t1 instanceof ISemanticProblem || t2 instanceof ISemanticProblem) {
            return UNKNOWN;
        }
        
        if (t1 instanceof IArrayType) {
            if (t2 instanceof IArrayType) {
                IArrayType a1= (IArrayType) t1;
                IArrayType a2= (IArrayType) t2;
                return isSameType(a1.getType(), a2.getType());
            }
            return FALSE;
        }
        
        if (t1 instanceof IBasicType) {
            if (t2 instanceof IBasicType) {
                IBasicType a1= (IBasicType) t1;
                IBasicType a2= (IBasicType) t2;
                if (a1.getKind() != a2.getKind()) {
                    return FALSE;
                }
                if (getSigned(a1) != getSigned(a2) || a1.isUnsigned() != a2.isUnsigned()) {
                    return FALSE;
                }
                if (a1.isLong() != a2.isLong() || a1.isShort() != a2.isShort()) {
                    return FALSE;
                }
                return TRUE;
            }
            return FALSE;
        }

        if (t1 instanceof ICompositeType) {
            if (t2 instanceof ICompositeType) {
                ICompositeType a1= (ICompositeType) t1;
                ICompositeType a2= (ICompositeType) t2;
                if (a1.getKey() != a2.getKey()) {
                    return FALSE;
                }
                return isSameScope(a1.getCompositeScope(), a2.getCompositeScope(), false);
            }
            return FALSE;
        }

        if (t1 instanceof ICPPReferenceType) {
            if (t2 instanceof ICPPReferenceType) {
                ICPPReferenceType a1= (ICPPReferenceType) t1;
                ICPPReferenceType a2= (ICPPReferenceType) t2;
                return isSameType(a1.getType(), a2.getType());
            }
            return FALSE;
        }

        if (t1 instanceof ICPPTemplateTypeParameter) {
            if (t2 instanceof ICPPTemplateTypeParameter) {
                return TRUE;
            }
            return FALSE;
        }

        if (t1 instanceof IEnumeration) {
            if (t2 instanceof IEnumeration) {
                IEnumeration a1= (IEnumeration) t1;
                IEnumeration a2= (IEnumeration) t2;
                
                return isSameScope(a1.getScope(), a2.getScope(), false);
            }
            return FALSE;
        }

        if (t1 instanceof IFunctionType) {
            if (t2 instanceof IFunctionType) {
                IFunctionType a1= (IFunctionType) t1;
                IFunctionType a2= (IFunctionType) t2;
                return hasSameSignature(a1, a2);
            }
            return FALSE;
        }

        if (t1 instanceof IPointerType) {
            if (t2 instanceof IPointerType) {
                IPointerType a1= (IPointerType) t1;
                IPointerType a2= (IPointerType) t2;
                if (a1.isConst() != a2.isConst() || a1.isVolatile() != a2.isVolatile() || a1.isRestrict() != a2.isRestrict()) {
                    return FALSE;
                }
                return isSameType(a1.getType(), a2.getType());
            }
            return FALSE;
        }

        if (t1 instanceof IQualifierType) {
            if (t2 instanceof IQualifierType) {
                IQualifierType a1= (IQualifierType) t1;
                IQualifierType a2= (IQualifierType) t2;
                if (a1.isConst() != a2.isConst() || a1.isVolatile() != a2.isVolatile()) {
                    return FALSE;
                }
                return isSameType(a1.getType(), a2.getType());
            }
            return FALSE;
        }
        
        return UNKNOWN;
    }

    private static boolean getSigned(IBasicType a2) {
        if (a2.isSigned()) {
            return true;
        }
        if (a2.isUnsigned()) {
            return false;
        }
        switch (a2.getKind()) {
        case eInt:
        case eUnspecified:
            return true;
        default:
        	break;
        }
        return false;
    }

    private static IType getRealType(IType t) {
        while (t instanceof ITypedef) {
            t= ((ITypedef) t).getType();
        }
        return t;
    }

    private static String getNameOrNull(IASTNode node) {
        if (node instanceof IASTDeclarator) {
            return getSimpleName(((IASTDeclarator) node).getName()).toString();
        }        
        if (node instanceof IASTNamedTypeSpecifier) {
            return getSimpleName(((IASTNamedTypeSpecifier) node).getName()).toString();
        }
        if (node instanceof IASTCompositeTypeSpecifier) {
            return getSimpleName(((IASTCompositeTypeSpecifier) node).getName()).toString();
        }
        if (node instanceof ICPPASTNamespaceDefinition) {
        	return getSimpleName(((ICPPASTNamespaceDefinition) node).getName()).toString();
        }
        if (node instanceof IASTTranslationUnit) {
            return ((IASTTranslationUnit) node).getFilePath();
        }
        return null;
    }

    private static int hasSameLocation(IASTNode node1, IASTNode node2, boolean fileStatic) {
        if (node1 == null || node2 == null) {
            return UNKNOWN;
        }
        if (!fileStatic && node1 instanceof IASTTranslationUnit &&
                node2 instanceof IASTTranslationUnit) {
            return TRUE;
        }
        
        IASTFileLocation l1= node1.getNodeLocations()[0].asFileLocation();
        IASTFileLocation l2= node2.getNodeLocations()[0].asFileLocation();
        if (l1 == null || l2 == null) {
            return UNKNOWN;
        }
        if (!l1.getFileName().equals(l2.getFileName())) {
            return FALSE;
        }
        if (l1.getNodeOffset() != l2.getNodeOffset()) {
            return FALSE;
        }
        if (l1.getNodeLength() != l2.getNodeLength()) {
            return FALSE;
        }
        return TRUE;
    }

    private static IScope getContainingScope(IASTName name) {
        IASTTranslationUnit tu= name.getTranslationUnit();
        if (tu == null) {
            return null;
        }
        if (tu instanceof ICPPASTTranslationUnit) {
            return CPPVisitor.getContainingScope(name);
        }
        return CVisitor.getContainingScope(name);
    }
        
    public static boolean isLocalVariable(IVariable v, IScope scope) {
        if (v instanceof IParameter) {
            return false;
        }
        while (scope != null) {
            if (scope instanceof ICPPFunctionScope ||
                    scope instanceof ICPPBlockScope ||
                    scope instanceof ICFunctionScope) {
                return true;
            }
            try {
                scope= scope.getParent();
            } catch (DOMException e) {
                scope= null;
            }
        }
        return false;
    }

    public static boolean isLocalVariable(IVariable v) {
        try {
            return isLocalVariable(v, v.getScope());
        } catch (DOMException e) {
            return false;
        }
    }

    public static IBinding[] findInScope(final IScope scope, String name, IASTTranslationUnit tu,
    		boolean removeGlobalsWhenClassScope) throws DOMException {
        IBinding[] result= null;
        result = scope.find(name, tu);
        if (result == null || result.length == 0) {
            return result;
        }
        
        // eliminate global bindings when looking up in a class type
        if (removeGlobalsWhenClassScope &&
                (scope instanceof ICPPClassScope || scope instanceof ICCompositeTypeScope)) {
            int count= 0;
            for (int i = 0; i < result.length; i++) {
                IBinding binding = result[i];
                IScope bscope= binding.getScope();
                if (!(bscope instanceof ICPPClassScope || bscope instanceof ICCompositeTypeScope)) {
                    result[i]= null;
                } else {
                    count++;
                }
            }
            if (count < result.length) {
                IBinding[] copy= new IBinding[count];
                int i= 0;
                for (IBinding b : result) {
                    if (b != null) {
                        copy[i++]= b;
                    }
                }
                result= copy;
            }
        }        
        
        // Try to find constructors.
        if (scope instanceof ICPPBlockScope) {
            for (int i = 0; i < result.length; i++) {
                IBinding binding = result[i];
                if (binding instanceof ICPPClassType) {
                    ICPPClassType classType= (ICPPClassType) binding;
                    if (classType.getKey() == ICPPClassType.k_class) {
                        IBinding[] cons= classType.getConstructors();
                        if (cons.length > 0 && ! (cons[0] instanceof IProblemBinding)) {
                            result[i]= cons[0];
                        }
                    }
                }
            }
        }

        return result;
    }
    
    public ASTManager(CRefactoringArgument arg) {
        fArgument= arg;
    }

	@Override
	public void dispose() {
        Assert.isTrue(!fDisposed, "ASTManager.dispose() called more than once"); //$NON-NLS-1$
		fDisposed = true;
		if (fSharedAST != null) {
			ASTProvider.getASTProvider().releaseSharedAST(fSharedAST);
		}
	}

	@Override
	protected void finalize() throws Throwable {
		if (!fDisposed)
			CUIPlugin.logError("ASTManager was not disposed"); //$NON-NLS-1$
		super.finalize();
	}

	void analyzeArgument(IIndex index, IProgressMonitor pm, RefactoringStatus status) {
        if (fArgument == null) {
            return;
        }
         
        if (fArgument.getArgumentKind() != CRefactory.ARGUMENT_UNKNOWN) {
            return;
        }
        
        if (fArgument.getSourceFile() == null)
            return;

        pm.beginTask(RenameMessages.ASTManager_task_analyze, 2);
        IASTTranslationUnit tu= getAST(index, fArgument.getSourceFile(), true, status);
        pm.worked(1);
        if (tu != null) {
        	final IASTNodeSelector nodeSelector = tu.getNodeSelector(tu.getFilePath());
			final int offset = fArgument.getOffset();
			final int length = fArgument.getLength();
			IASTName name= nodeSelector.findEnclosingName(offset, length);
        	if (name != null) {
    			name= name.getLastName();
        	} else {
        		IASTNode node= nodeSelector.findEnclosingNode(offset, length);
        		if (node instanceof IASTPreprocessorMacroDefinition ||
        				node instanceof IASTPreprocessorElifStatement ||
        				node instanceof IASTPreprocessorIfdefStatement ||
        				node instanceof IASTPreprocessorIfndefStatement ||
        				node instanceof IASTPreprocessorIfStatement) {
        			final IASTFileLocation fileLocation = node.getFileLocation();
        			if (fileLocation != null) {
        				final String ident= extractIdentifier(node.getRawSignature(),
        						offset - fileLocation.getNodeOffset(), length);
        				if (ident != null) {
        					IASTPreprocessorMacroDefinition[] mdefs= tu.getMacroDefinitions();
        					for (IASTPreprocessorMacroDefinition mdef : mdefs) {
        						IASTName n= mdef.getName();
        						if (ident.equals(n.toString())) {
        							name= n;
        							break;
        						}
        					}
        				}
        			}
        		}
        	}
        	if (name != null) {
        		fArgument.setName(name);
        		IBinding binding= name.resolveBinding();
        		if (binding != null) {
        			IScope scope= null;
        			try {
        				scope = binding.getScope();
        			} catch (DOMException e) {
        				handleDOMException(tu, e, status);
        			}
        			fArgument.setBinding(name.getTranslationUnit(), binding, scope);
        		}
        	}
        }
        pm.worked(1);
        pm.done();
    }

	private String extractIdentifier(String rawSignature, int offset, int length) {
		char[] sig= rawSignature.toCharArray();
		int end= offset + length;
		if (offset < 0 || end > sig.length)
			return null;
		
		for (int i = offset; i < end; i++) {
			if (!Character.isJavaIdentifierPart(sig[i]))
				return null;
		}
		while (offset > 0) {
			if (!Character.isJavaIdentifierPart(sig[offset - 1]))
				break;
			offset--;
		}
		while (end < sig.length) {
			if (!Character.isJavaIdentifierPart(sig[end]))
				break;
			end++;
		}
		return rawSignature.substring(offset, end);
	}

	/**
	 * Returns an AST for the given file.
	 *
	 * @param index the index to use for the AST
	 * @param sourceFile the source file to obtain an AST for
	 * @param astStyle the style to pass to {@link ITranslationUnit#getAST(IIndex, int)} method.
	 *     If a previously cached AST is returned, the style is not guaranteed to match
	 *     the requested one.
	 * @param cacheIt if {@code true}, the AST will be cached for later reuse 
	 * @return the requested AST or {@code null}
	 * @throws CoreException
	 */
	public synchronized IASTTranslationUnit getAST(IIndex index, IFile sourceFile, int astStyle,
			boolean cacheIt) throws CoreException {
        IASTTranslationUnit ast=  fTranslationUnits.get(sourceFile);
        if (ast == null) {
            ICElement celem= CoreModel.getDefault().create(sourceFile);
            if (celem instanceof ITranslationUnit) {
            	ITranslationUnit tu= CModelUtil.toWorkingCopy((ITranslationUnit) celem);
        		if (fSharedAST != null && tu.equals(fSharedAST.getOriginatingTranslationUnit())) {
        			ast = fSharedAST;
        		} else {
                	// Try to get a shared AST before creating our own.
    	        	ast = ASTProvider.getASTProvider().acquireSharedAST(tu, index,
    	        			ASTProvider.WAIT_ACTIVE_ONLY, null);
    	        	if (ast != null) {
    	        		if (fSharedAST != null) {
    	        			ASTProvider.getASTProvider().releaseSharedAST(fSharedAST);
    	        		}
    	        		fSharedAST = ast;
    	        	} else {
						ast= tu.getAST(index, astStyle);
    	            	if (cacheIt) {
    	            		fTranslationUnits.put(sourceFile, ast);
    	            	}
    	        	}
        		}
            }
        }
        return ast;
    }

	private IASTTranslationUnit getAST(IIndex index, IFile sourceFile, boolean cacheIt,
			RefactoringStatus status) {
		try {
			return getAST(index, sourceFile, PARSE_MODE, cacheIt);
		} catch (CoreException e) {
			status.addError(e.getMessage());
			return null;
		}
    }

	public void analyzeTextMatches(IIndex index, Collection<CRefactoringMatch> matches,
    		IProgressMonitor monitor, RefactoringStatus status) {
        CRefactoringMatchStore store= new CRefactoringMatchStore();
        for (CRefactoringMatch match : matches) {
            store.addMatch(match);
        }
        
        int count= store.getFileCount();
        String taskName= RenameMessages.ASTManager_task_generateAst;
        monitor.beginTask(taskName, 2 * count);
        monitor.setTaskName(taskName);

        List<IFile> files= store.getFileList();
        int cc= 0;
        long now= System.currentTimeMillis();
        long update= now;
        for (IFile file : files) {
            cc++;
            if (store.contains(file)) {
                if ((now = System.currentTimeMillis()) > update) {
                    String nofm= nth_of_m(cc, count);
                    String taskname= NLS.bind(RenameMessages.ASTManager_subtask_analyzing, nofm);
                    monitor.subTask(taskname); 
                    update= now + 1000;
                }
                boolean doParse= false;
                Collection<CRefactoringMatch> fm= store.getMatchesForFile(file);
                for (Iterator<CRefactoringMatch> iterator = fm.iterator(); !doParse && iterator.hasNext();) {
                    CRefactoringMatch match = iterator.next();
                    switch (match.getLocation()) {
                    case CRefactory.OPTION_IN_COMMENT:
                    case CRefactory.OPTION_IN_INCLUDE_DIRECTIVE:
                    case CRefactory.OPTION_IN_STRING_LITERAL:
                        break;
                    default:
                        doParse= true;
                    }
                }

                if (doParse) {
                    IASTTranslationUnit tu= getAST(index, file, false, status);
                    monitor.worked(1);
                    analyzeTextMatchesOfTranslationUnit(tu, store, status);
                    if (status.hasFatalError()) {
                        return;
                    }
                    monitor.worked(1);
                } else {
                    monitor.worked(2);
                }
                if (monitor.isCanceled()) {
                    throw new OperationCanceledException();
                }
            } else {
                monitor.worked(2);
            }
        }
        monitor.done();
    }

    private void analyzeTextMatchesOfTranslationUnit(IASTTranslationUnit tu, 
            final CRefactoringMatchStore store, final RefactoringStatus status) {
        fKnownBindings= new HashMap<>();
        fConflictingBinding= new HashSet<>();
        final Set<IPath> paths= new HashSet<>();
        boolean renamesMacro= fArgument.getArgumentKind() == CRefactory.ARGUMENT_MACRO;
        
        analyzeMacroMatches(tu, store, paths, status);
        if (status.hasFatalError()) return;

        if (renamesMacro) {
            findConflictingBindingsWithNewName(tu, store, paths, status);
            if (status.hasFatalError()) return;
        }

        analyzeLanguageMatches(tu, store, paths, status);
        if (status.hasFatalError()) return;

        for (IPath path : paths) {
            if (path != null) {
                store.removePath(path);
            }
        }
        handleConflictingBindings(tu, status);
        fKnownBindings= null;
        fConflictingBinding= null;
    }

    private void analyzeLanguageMatches(IASTTranslationUnit tu, final CRefactoringMatchStore store,
    		final Set<IPath> paths, final RefactoringStatus status) {
        ASTNameVisitor nv = new ASTSpecificNameVisitor(fArgument.getName()) {
            @Override
			protected int visitName(IASTName name, boolean isDestructor) {
                IPath path= analyzeAstMatch(name, store, isDestructor, status);
                paths.add(path);
                return ASTVisitor.PROCESS_CONTINUE;
            }
        };
        tu.accept(nv);
    }

    private void analyzeMacroMatches(IASTTranslationUnit tu, final CRefactoringMatchStore store,
    		final Set<IPath> pathsVisited, final RefactoringStatus status) {
        String lookfor= fArgument.getName();
        IASTPreprocessorMacroDefinition[] mdefs= tu.getMacroDefinitions();
        for (IASTPreprocessorMacroDefinition mdef : mdefs) {
            IASTName macroName= mdef.getName();
            String macroNameStr= macroName.toString();
            if (fRenameTo.equals(macroNameStr)) {
                status.addFatalError(NLS.bind(RenameMessages.ASTManager_error_macro_name_conflict, fRenameTo));
                return;
            } else if (lookfor.equals(macroNameStr)) {
                IPath path= analyzeAstMatch(macroName, store, false, status);
                pathsVisited.add(path);
                IBinding macroBinding= macroName.resolveBinding();
                if (macroBinding != null) {
                    IASTName[] refs= tu.getReferences(macroBinding);
                    for (IASTName ref : refs) {
                        path= analyzeAstMatch(ref, store, false, status);
                        pathsVisited.add(path);
                    }
                }
            }
            if (mdef instanceof IASTPreprocessorFunctionStyleMacroDefinition) {
                boolean nameIsPar= false;
                IASTPreprocessorFunctionStyleMacroDefinition fm= (IASTPreprocessorFunctionStyleMacroDefinition) mdef;
                IASTFunctionStyleMacroParameter[] pars= fm.getParameters();
                if (pars != null) {
                    for (int j = 0; !nameIsPar && j<pars.length; j++) {
                        IASTFunctionStyleMacroParameter par = pars[j];
                        String name= par.getParameter();
                        if (lookfor.equals(name)) {
                            nameIsPar= true;
                        }
                    }
                    if (nameIsPar) {
                        IASTFileLocation floc= mdef.getNodeLocations()[0].asFileLocation();
                        int offset= floc.getNodeOffset();
                        int end= offset+ floc.getNodeLength();
                        Collection<CRefactoringMatch> matches= store.findMatchesInRange(
                                new Path(floc.getFileName()), offset, end);
                        for (CRefactoringMatch match : matches) {
                            match.setASTInformation(CRefactoringMatch.AST_REFERENCE_OTHER);
                        }
                    }
                }
            }
        }
    }

//    private void markPreprocessorMatchesAsReference(
//            IASTTranslationUnit tu, final CRefactoringMatchStore store, 
//            final Set pathsVisited, final RefactoringStatus status) {
//        IASTPreprocessorStatement[] pdefs= tu.getAllPreprocessorStatements();
//        for (int i = 0; i < pdefs.length; i++) {
//            IASTPreprocessorStatement pdef = pdefs[i];
//            if (pdef instanceof IASTPreprocessorIfdefStatement
//                    || pdef instanceof IASTPreprocessorIfndefStatement
//                    || pdef instanceof IASTPreprocessorIfStatement
//                    || pdef instanceof IASTPreprocessorElifStatement
////                  || pdef instanceof IASTPreprocessorElseStatement
//                    || pdef instanceof IASTPreprocessorUndefStatement) {
//        IPath path= new Path(tu.getContainingFilename());
//                if (!store.getMatchesForPath(path).isEmpty()) {
//                    IASTFileLocation floc= pdef.getNodeLocations()[0].asFileLocation();
//                    int offset= floc.getNodeOffset();
//                    int end= offset+ floc.getNodeLength();
//                    Collection matches= store.findMatchesInRange(
//                            new Path(floc.getFileName()), offset, end);
//                    for (Iterator iter = matches.iterator(); iter.hasNext();) {
//                        CRefactoringMatch match = (CRefactoringMatch) iter.next();
//                        match.setASTInformation(CRefactoringMatch.AST_REFERENCE);
//                    }
//                }
//            }
//        }
//    }

    private void findConflictingBindingsWithNewName(IASTTranslationUnit tu, CRefactoringMatchStore store,
    		final Set<IPath> paths, final RefactoringStatus status) {
        ASTNameVisitor nv = new ASTSpecificNameVisitor(fRenameTo) {
            @Override
			protected int visitName(IASTName name, boolean isDestructor) {
                IPath path= addConflictingBindingForName(status, name);
                paths.add(path);
                return ASTVisitor.PROCESS_CONTINUE;
            }
        };
        tu.accept(nv);
    }

    protected IPath addConflictingBindingForName(final RefactoringStatus status, IASTName name) {
        IASTNodeLocation[] locations= name.getNodeLocations();
        IPath path= null;
        if (locations != null && locations.length == 1) {
            IASTNodeLocation loc= locations[0];
            IASTFileLocation floc= loc.asFileLocation();
            if (floc != null) {
                path= new Path(floc.getFileName());
                IBinding binding= name.resolveBinding();
                if (binding instanceof IProblemBinding) {
                	handleProblemBinding(name.getTranslationUnit(), (IProblemBinding) binding, status);
                } else if (binding != null) {
            		fConflictingBinding.add(binding);
                }
            }
        }
        return path;
    }

    protected IPath analyzeAstMatch(IASTName name, CRefactoringMatchStore store, boolean isDestructor,
    		RefactoringStatus status) {
        IPath path= null;
        CRefactoringMatch match= null;
        
        IASTFileLocation loc = getImageFileLocation(name);
        if (loc != null) {
        	path= new Path(loc.getFileName());
        	match= store.findMatch(path, loc.getNodeOffset() + (isDestructor ? 1 : 0));
        	if (match != null) {
        		analyzeAstTextMatchPair(match, name, status);
        	}
        }
        return path;
    }

	static IASTFileLocation getImageFileLocation(IASTName name) {
		return name.getImageLocation();
	}

    private void analyzeAstTextMatchPair(CRefactoringMatch match, IASTName name, RefactoringStatus status) {
        IBinding binding= name.resolveBinding();
        int cmp= FALSE;
        Integer cmpObj= fKnownBindings.get(binding);
        if (cmpObj != null) {
            cmp= cmpObj.intValue();
        } else if (binding instanceof IProblemBinding) {
            cmp= UNKNOWN;
            handleProblemBinding(name.getTranslationUnit(), (IProblemBinding) binding, status);
        } else {
        	// Check whether a qualifier has a problem binding.
        	boolean problemInQualifier= false;
        	IASTNode parent= name.getParent();
        	if (parent instanceof ICPPASTQualifiedName) {
        		ICPPASTNameSpecifier[] qualifier = ((ICPPASTQualifiedName) parent).getQualifier();
        		for (ICPPASTNameSpecifier n : qualifier) {
					if (n == name)
						break;
					final IBinding b = n.resolveBinding();
					if (b instanceof IProblemBinding) {
						handleProblemBinding(name.getTranslationUnit(), (IProblemBinding) b, status);
						problemInQualifier= true;
						break;
					}
				}
        	}
        	if (problemInQualifier) {
        		cmp= UNKNOWN;
        	} else {
    			final IASTTranslationUnit tu = name.getTranslationUnit();
    			final IIndex index= tu != null ? tu.getIndex() : null;
    			IBinding[] bindings = binding instanceof ICPPUsingDeclaration ?
        				((ICPPUsingDeclaration) binding).getDelegates() : new IBinding[] { binding };
   				// When a 'using' declaration has multiple delegate bindings and only some of them
        		// are being renamed, to preserve correctness of the code we would have to split
        		// the 'using' declaration into two separate ones. We currently don't do that and
        		// rename the 'using' declaration if at least one of its delegate bindings is being
        		// renamed.
        		outer: for (IBinding b : bindings) {
        			for (IBinding renameBinding : fValidBindings) {
	        			try {
	        				int cmp0= isSameBinding(index, b, renameBinding);
	        				if (cmp0 != FALSE) {
	        					cmp= cmp0;
	        				}
	        				if (cmp0 == TRUE) {
	        					break outer;
	        				}
	        			} catch (DOMException e) {
	        				handleDOMException(name.getTranslationUnit(), e, status);
	        				cmp= UNKNOWN;
	        			}
        			}
        		}
        	}
            fKnownBindings.put(binding, Integer.valueOf(cmp));
        }
        switch (cmp) {
        case TRUE:
            match.setASTInformation(CRefactoringMatch.AST_REFERENCE);
            if (fRenameTo != null) {
                IScope scope= getContainingScope(name);
                if (scope != null) {
                    IBinding[] conflicting= null;
                    try {
                        conflicting= findInScope(scope, fRenameTo, name.getTranslationUnit(), true);
                    } catch (Exception e) {
                    	CUIPlugin.log(e);
                    }
                    if (conflicting != null && conflicting.length > 0) {
                        fConflictingBinding.addAll(Arrays.asList(conflicting));
                    }
                }
            }
            break;
        case FALSE:
            match.setASTInformation(CRefactoringMatch.AST_REFERENCE_OTHER);
            break;
        }
    }

    public void handleDOMException(IASTTranslationUnit tu, final DOMException e, RefactoringStatus status) {
        handleProblemBinding(tu, e.getProblem(), status);
    }

    public void handleProblemBinding(IASTTranslationUnit tu, final IProblemBinding pb,
    		RefactoringStatus status) {
        if (tu != null) {
            String fpath= tu.getFilePath();
            if (fProblemUnits.add(fpath)) {
                String msg= pb.getMessage();
                if (msg != null && msg.length() > 0) {
                    msg= NLS.bind(RenameMessages.ASTManager_warning_parsingError_detailed, msg);
                } else {
                    msg= RenameMessages.ASTManager_warning_parsingError;
                }
                int line= pb.getLineNumber();
                if (line >= 1) {
                    msg= NLS.bind(RenameMessages.ASTManager_warning_parsingError_withFileAndLine,
                    		new Object[] { msg, fpath, line });
                } else {
                    msg= NLS.bind(RenameMessages.ASTManager_warning_parsingError_withFile, msg, fpath);
                }
                status.addWarning(msg);
            }
        }
    }

	@SuppressWarnings("unchecked")
	protected void handleConflictingBindings(IASTTranslationUnit tu, RefactoringStatus status) {   
        if (fConflictingBinding.isEmpty()) {
            return;
        }
        
        int argKind= fArgument.getArgumentKind();
        boolean isVarParEnumerator= false;
        boolean isLocalVarPar= false;
        boolean isFunction= false;
        boolean isContainer = false;
        boolean isMacro= false;

        switch (argKind) {
        case CRefactory.ARGUMENT_LOCAL_VAR:  
        case CRefactory.ARGUMENT_PARAMETER:
            isLocalVarPar= true;
            isVarParEnumerator= true;
            break;
        case CRefactory.ARGUMENT_FILE_LOCAL_VAR:    
        case CRefactory.ARGUMENT_GLOBAL_VAR:
        case CRefactory.ARGUMENT_FIELD:     
        case CRefactory.ARGUMENT_ENUMERATOR:         
            isVarParEnumerator= true;
            break;
        case CRefactory.ARGUMENT_FILE_LOCAL_FUNCTION:
        case CRefactory.ARGUMENT_GLOBAL_FUNCTION:
        case CRefactory.ARGUMENT_VIRTUAL_METHOD:     
        case CRefactory.ARGUMENT_NON_VIRTUAL_METHOD:
            isFunction= true;
            break;
        case CRefactory.ARGUMENT_TYPE:
        case CRefactory.ARGUMENT_CLASS_TYPE:
        case CRefactory.ARGUMENT_NAMESPACE:
            isContainer = true;
            break;
        case CRefactory.ARGUMENT_MACRO:      
            isMacro= true;
            break;
        case CRefactory.ARGUMENT_INCLUDE_DIRECTIVE:  
            break;
        }
        
        Collection<IBinding>[] cflc=
        		new Collection[] { new HashSet<IBinding>(), new ArrayList<IBinding>(),
        		                  new ArrayList<IBinding>() };
        String[] errs= null;
        if (isMacro) {
            errs= new String[] { RenameMessages.CRenameLocalProcessor_error_conflict };
            cflc[0]= fConflictingBinding;
        } else {
            errs= new String[] {
                    RenameMessages.CRenameLocalProcessor_error_shadow,
                    RenameMessages.CRenameLocalProcessor_error_redeclare,
                    RenameMessages.CRenameLocalProcessor_error_isShadowed,
                    RenameMessages.CRenameLocalProcessor_error_overloads };
            classifyConflictingBindings(tu, (Set<IBinding>) cflc[0], (List<IBinding>) cflc[1],
            			(List<IBinding>) cflc[2], status);
        }
        
        for (int i = 0; i < 3; i++) {
            Collection<?> coll= cflc[i];
            for (Object name : coll) {
                boolean warn= false;
                String msg= errs[i];
                IBinding conflict = (IBinding) name;
                String what= null;
                if (conflict instanceof IEnumerator) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        what= RenameMessages.CRenameLocalProcessor_enumerator;
                    }
                } else if (conflict instanceof ICPPField) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        what= RenameMessages.CRenameLocalProcessor_field;
                    }
                } else if (conflict instanceof IParameter) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        if (i == 1 && argKind == CRefactory.ARGUMENT_LOCAL_VAR) {
                            msg= errs[0];
                        }
                        what= RenameMessages.CRenameLocalProcessor_parameter;
                    }
                } else if (conflict instanceof IVariable) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        IVariable conflictingVar= (IVariable) conflict;
                        what= RenameMessages.CRenameLocalProcessor_globalVariable;
                        if (ASTManager.isLocalVariable(conflictingVar)) {
                            if (i == 1 && argKind == CRefactory.ARGUMENT_PARAMETER) {
                                msg= errs[2];
                            }
                            what= RenameMessages.CRenameLocalProcessor_localVariable;
                        } else {
                            if (conflictingVar.isStatic()) {
							    what= RenameMessages.CRenameProcessorDelegate_fileStaticVariable;
							}
                        }
                    }
                } else if (conflict instanceof ICPPConstructor) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        what= RenameMessages.CRenameLocalProcessor_constructor;
                    }
                } else if (conflict instanceof ICPPMethod) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        if (i == 1) {
                            IBinding r= fArgument.getBinding();
                            if (r instanceof ICPPMethod) {
                                try {
                                    if (ASTManager.hasSameSignature((ICPPMethod) r, 
                                            (ICPPMethod) conflict) == ASTManager.FALSE) {
                                        msg= errs[3];
                                        warn= true;
                                    }
                                } catch (DOMException e) {
                                }
                            }
                        }
                        what= RenameMessages.CRenameLocalProcessor_method;
                    }
                } else if (conflict instanceof IFunction) {
                    if (isVarParEnumerator || isFunction || isMacro) {
                        boolean ignore= false;
                        if (isLocalVarPar) {
                            IASTName[] refs= 
                                fArgument.getTranslationUnit().getReferences(conflict);
                            if (refs == null || refs.length == 0) {
                                ignore= true;
                            }
                        }
                        if (!ignore) {
                            IFunction conflictingFunction= (IFunction) conflict;
                            if (i == 1 && conflict instanceof ICPPFunction) {
                                IBinding r= fArgument.getBinding();
                                if (r instanceof ICPPFunction) {
                                    try {
                                        if (ASTManager.hasSameSignature((ICPPFunction) r, 
                                                conflictingFunction) == ASTManager.FALSE) {
                                            msg= errs[3];
                                            warn= true;
                                        }
                                    } catch (DOMException e) {
                                    }
                                }
                            }

                            boolean isStatic= conflictingFunction.isStatic();
                            if (isStatic) {
                                what= RenameMessages.CRenameProcessorDelegate_fileStaticFunction;
                            } else {
                                what= RenameMessages.CRenameProcessorDelegate_globalFunction;
                            }
                        }
                    }
                } else if (conflict instanceof ICompositeType ||
                        conflict instanceof IEnumeration ||
                        conflict instanceof ITypedef) {
                    if (isContainer || isMacro) {
                        what= RenameMessages.CRenameProcessorDelegate_type;
                    }
                } else if (conflict instanceof ICPPNamespace) {
                    if (isContainer || isMacro) {
                        what= RenameMessages.CRenameProcessorDelegate_namespace;
                        if (argKind == CRefactory.ARGUMENT_NAMESPACE) {
                            warn= true;
                        }
                    }
                }
                if (what != null) {
                	String message = RenameMessages.CRenameLocalProcessor_error_message;
                	String message1 = NLS.bind(RenameMessages.CRenameLocalProcessor_error_message1, msg);
                	String message2 = NLS.bind(RenameMessages.CRenameLocalProcessor_error_message2, conflict.getName());
                	String message3 = NLS.bind(RenameMessages.CRenameLocalProcessor_error_message3, what);
                	String space = "  \n"; //$NON-NLS-1$
                	String formatted = message + space + message1 + space + message2 + space +  message3;
                    RefactoringStatusEntry[] entries= status.getEntries();
                    for (RefactoringStatusEntry entry : entries) {
                        if (formatted.equals(entry.getMessage())) {
                            formatted= null;
                            break;
                        }
                    }
                    if (formatted != null) {
                        if (warn) {
                            status.addWarning(formatted);
                        } else {
                            status.addError(formatted);
                        }
                    }
                }
            }
        }
    }

    protected void classifyConflictingBindings(IASTTranslationUnit tu, Set<IBinding> shadows,
    		Collection<IBinding> redecl, Collection<IBinding> barriers, RefactoringStatus status) {
        // Collect bindings on higher or equal level.
        String name= fArgument.getName();
        IBinding[] newBindingsAboverOrEqual= null;
        IScope oldBindingsScope= null;
        for (Map.Entry<IBinding, Integer> entry : fKnownBindings.entrySet()) {
            IBinding oldBinding= entry.getKey();
            Integer value= entry.getValue();
            if (value.intValue() == TRUE && oldBinding.getName().equals(name)) {
                try {
                    oldBindingsScope = oldBinding.getScope();
                    if (oldBindingsScope != null) {
                        newBindingsAboverOrEqual = ASTManager.findInScope(oldBindingsScope, fRenameTo, null, false);
                    }
                } catch (DOMException e) {
                    handleDOMException(tu, e, status);
                }
            }            

            if (newBindingsAboverOrEqual != null && newBindingsAboverOrEqual.length > 0) {
                break;
            }
        }
        if (newBindingsAboverOrEqual == null) {
            newBindingsAboverOrEqual= IBinding.EMPTY_BINDING_ARRAY;
        }
        
        // Check conflicting bindings for being from above or equal level.
        for (IBinding conflictingBinding : fConflictingBinding) {
            if (conflictingBinding != null) {
                boolean isAboveOrEqual= false;
                for (int i = 0; !isAboveOrEqual && i < newBindingsAboverOrEqual.length; i++) {
                    IBinding aboveBinding = newBindingsAboverOrEqual[i];
                    try {
                        if (isSameBinding(tu.getIndex(), aboveBinding, conflictingBinding) == TRUE) {
                            isAboveOrEqual= true;
                        }
                    } catch (DOMException e) {
                        handleDOMException(tu, e, status);
                    }
                }
                if (!isAboveOrEqual) {
                    barriers.add(conflictingBinding);
                }
            }
        }

        // Find bindings on same level.
        for (IBinding aboveBinding : newBindingsAboverOrEqual) {
            IScope aboveScope;
            try {
                aboveScope = aboveBinding.getScope();
                if (isSameScope(aboveScope, oldBindingsScope, false) == TRUE) {
                    redecl.add(aboveBinding);
                } else {
                    shadows.add(aboveBinding);
                }
            } catch (DOMException e) {
                handleDOMException(tu, e, status);
            }
        }
    }

    public void setValidBindings(IBinding[] validBindings) {
        fValidBindings= validBindings;
    }

    public void setRenameTo(String renameTo) {
        fRenameTo= renameTo;
    }
}
